/**
 * @class   wosh::Bus
 * @brief   Core interface, it allows services, devices or any IBusListener
 *          implementation, to listen for any or particular messages and inject
 *          new messages.
 *          Posting is usually asynchronous, message delivery is transient (
 *          destination must be subscribed and working).
 *
 *
 * @see     wosh::BusManager
 ****************************************************************************
 * @version $Id: Bus.h 2877 2010-08-09 00:02:53Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

#ifndef __WOSH_Core_Bus_H__
 #define __WOSH_Core_Bus_H__

 #include <woshDefs.h>
 #include <core/Object.h>
 #include <core/Containers.h>
 #include <core/MessageQueue.h>
 #include <core/LogContext.h>
 #include <core/Timer.h>


namespace wosh {

 #define _Bus_NAME			"wosh.Bus"
 #define _Bus_ANY			"*"

 #define _Bus_Core			"wosh.Bus.Core"
 #define _Bus_Devices		"wosh.Bus.Devices"
 #define _Bus_Security		"wosh.Bus.Security"
 #define _Bus_DSM			"wosh.Bus.DSM"

 #define _Bus_Building		"wosh.Bus.Building"
 #define _Bus_Automation	"wosh.Bus.Automation"
 #define _Bus_Gui			"wosh.Bus.Gui"

#define WOSH_BUS(CLASSNAME)						\
		WOSH_CLASSNAME(CLASSNAME)				\
		WOSH_CLASS_ABSTRACT(false)


class BusManager;
class IBusListener;
template <class T> class DataFieldCollector;

class Bus : public Object
 {
	WOSH_CLASSNAME(wosh::Bus)
	WOSH_CLASS_ABSTRACT(false)

	typedef ListT<IBusListener*> tIBusListenerList;

	public:
		/// types of message's content
		enum BUS_STATE {
			STATE_UNKNOWN	= 0x00,
			STATE_CREATED	= 0x01,
			STATE_STARTED	= 0x02,
			STATE_STOPPED	= 0x04,
			STATE_DESTROYED = 0x08
		 };
		/// options of connect/disconnect
		enum BUS_CONNECTION_OPTIONS {
			BUS_NO_OPTIONS			= 0x00,
			BUS_CREATE_IF_MISSING	= 0x01,
			ACTION_DEFERRED			= 0x02
		 };

	public:
		/**
		 * @brief  Empty constructor. Assign the ID, name and type of the Object, init Log.
		 */
		Bus();
		/**
		 * @brief  Empty destructor.
		 */
		virtual ~Bus();


/** @name Bus interface - management
 * @{
 ******************************************************************************/
	public:
		/**
		 * @brief  Start the Bus (internal thread).
		 * @param  timeout [in] timeout in milliseconds, 0 means asynch call; default is no timeout
		 * @return WRET_ERR_ILLEGAL_USE if Bus is already running, WRET_ERR_INTERNAL when
		 *         request was synchronous and errors were found; WRET_OK else
		 * @note   Implementation must be thread safe.
		 */
		virtual WRESULT startBus( unsigned long timeout = LIMIT_ULONG_MAX ) = 0;

		/**
		 * @brief  Stop the Bus (internal thread). Should be synchronous.
		 * @param  timeout [in] timeout in milliseconds, 0 means asynch call; default is no timeout
		 * @return WRET_ERR_ILLEGAL_USE if Bus is already running; WRET_ERR_INTERNAL when
		 *         errors were found; WRET_OK else
		 * @note   Implementation must be thread safe.
		 */
		virtual WRESULT stopBus( unsigned long timeout = LIMIT_ULONG_MAX ) = 0;

		/**
		 * @brief  Evaluate if Bus is running (working). Posting should be asynchronous.
		 * @return true if Bus is running, false else
		 * @note   Implementation must be thread safe.
		 */
		virtual bool isBusRunning() const = 0;

//@}
/** @name Bus interface - messages
 * @{
 ******************************************************************************/
	public:
		/**
		 * @brief  Post a message on the Bus (forward message to handlers).
		 * @param [in] message the message to be injected on Bus, object is not
		 *             copied, instance will be deleted by Bus when all handlers
		 *             received the message.
		 * @note   Implementation must be thread safe.
		 */
		virtual void postMessage( Message* message ) const;

		/**
		 * @brief  Get the current size of the Message queue.
		 * @return number of messages in the queue
		 * @note   Implementation must be thread safe.
		 */
		virtual int getQueueSize() const	{ return this->msgQueue.size(); }

		/**
		 * @brief  Clean the Message queue.
		 * @note   Implementation must be thread safe.
		 */
		virtual void flushMessages();

//@}
/** @name Bus interface - handlers
 * @{
 ******************************************************************************/
	public:
		/**
		 * @brief  Register a IBusListener object from the list. Reentrant.
		 * @param [in] node the instance to be added from list
		 * @return WRET_ERR_PARAM when argument is NULL; WRET_ERR_ILLEGAL_USE if
		 *         argument is already registered; WRET_OK when operation succeded.
		 * @note   When called within busMessage() deferred_add must be true or it will dead-lock
		 */
		virtual WRESULT addBusListener( IBusListener* node, bool deferred_add = true );

		/**
		 * @brief  UnRegister a IBusListener object from the list
		 * @param [in] node the instance to be removed from list
		 * @return WRET_ERR_PARAM when argument is NULL; WRET_ERR_ILLEGAL_USE if
		 *         argument is not registered; WRET_OK when operation succeded.
		 * @note   When called within busMessage() deferred_add must be true or it will dead-lock
		 */
		virtual WRESULT removeBusListener( IBusListener* node, bool deferred_remove = false );

		/**
		 * @brief  Evaluate if a IBusListener object is registered as listener.
		 * @param [in] node the instance to be checked
		 * @return true if IBusListener object is registered, false else
		 * @note   Implementation must be thread safe.
		 */
		virtual bool isBusListener( const IBusListener* node ) const;

		/**
		 * @brief  Count registered message listeners.
		 * @return the number of registered IBusListener objects
		 * @note   Implementation must be thread safe.
		 */
		virtual inline int countBusListeners() const	{ return this->nodes.count(); }

//@}
/** @name Getters
 * @{
 ******************************************************************************/
	public:
		/**
		 * @brief  Get unique ID of the Bus
		 * @return unique identifier of the Bus
		 */
		inline long getBusID() const 					{ return this->busID; }

		/**
		 * @brief  Evaluate if Bus is shared over the Network. When flag is enabled
		 *         and Network Handler is set, Bus should forward messages to it (after local handlers). 
		 * @return true when Bus is shared, false else
		 * @see    wosh::NetworkManager
		 */
		virtual inline bool isSharedNetwork() const		{ return this->sharedNetwork; }

		virtual inline bool isSharedLocal() const		{ return this->sharedLocal; }

		static DataFieldCollector<Bus>* getDataFields();

		double getDutyCycleAverage() const;
		double getDutyCycleLast() const;

//@}
/** @name Setters
 * @{
 ******************************************************************************/
	public:
		virtual void setName( const std::string& value );

		inline void setSharedNetwork( bool value )		{ this->sharedNetwork = value; }
		inline void setSharedLocal( bool value )		{ this->sharedLocal = value; }

		using Object::setParentObject;

//@}
/** @name Internal methods
 * @{
 ******************************************************************************/
	protected:
		virtual void processingInitialize();
		virtual void processingStarted();
		virtual void processingFinished();

		virtual void fixDeferredListeners();

		/**
		 * @brief  Assign source Kernel to the message when empty. Provided for convenience.
		 *         Should be called by implementation when accepting new messages (postMessage).
		 * @param [in] message the message object to be updated
		 */
		void assignSourceKernel( Message* message ) const;

		/**
		 * @brief  Assign current Bus to the message, set source Bus when unset, set destination
		 *         Bus when empty. Provided for convenience.
		 *         Should be called by implementation when accepting new messages (postMessage).
		 * @param [in] message the message object to be updated
		 */
		void assignBus( Message* message ) const;

		/**
		 * @brief  Assign (unique) ID to the message. Provided for convenience.
		 *         Should be called by implementation when accepting new messages (postMessage)
		 *         and when ID of the Message is 0 (unset).
		 * @param [in] message the message object to be updated.
		 * @param [in] id the unique ID
		 */
		void assignID( Message* message, long id = 0 ) const;

		/**
		 * @brief  Raise an event to the BusManager.
		 * @param [in] state the state of the Bus
		 * @see    BusManager
		 */
		void push_event( Bus::BUS_STATE state );

		/**
		 * @brief  Push a Message to the BusManager. (it will route to destination, when required) 
		 * @param [in] message the message to be sent
		 * @see    BusManager
		 */
		void push_message( const Message* message );

		/**
		 * @brief  Check if destination bus is current bus (this).
		 * @param [in] message the message to be evaluated
		 * @return true when destination bus is current bus
		 */
		inline bool isForMyself( const Message* message ) const {
			if ( message == NULL ) return false;
			if ( message->getDestinationBus() == _Bus_ANY ) return true;
			if ( message->getDestinationBus() == getName() ) return true;
			return false;
		 }

//@}
/** @name Internal vars
 * @{
 ******************************************************************************/
	protected:
		mutable MessageQueue msgQueue;				///< messages' queue
		mutable tIBusListenerList nodes;			///< current Listeners (message handlers) of the Bus

		mutable tIBusListenerList nodes_add;		///< 
		mutable tIBusListenerList nodes_remove;		///< 

	protected:
		mutable LogContext Log;				///< my custom Logger
		bool sharedLocal;				///< flag, when true BusManager will route messages to/from..
		bool sharedNetwork;				///< flag, when true BusManager & NetworkManager will share bus on network

		MutexRW statsMux;
		mutable Timer dutyCycleTimer;	///< duty cycle stats: helper for timstamping
		mutable double dutyCycleLast;	///< duty cycle stats: last sample
		mutable double dutyCycleAverage;///< duty cycle stats: mean average of samples
		mutable double dutyCycleCount;	///< duty cycle stats: number of samples

	private:
		long busID;						///< Unique ID of the BUS

		friend class wosh::BusManager;	///< let our 'manager' access protected functions

	private:
		Bus(const Bus&) : Object() { }
		Bus& operator=(const Bus&) { return *this; }

//@}


}; // class def


/**
 * @class   wosh::IBusListener
 * @brief   Interface for Objects which listens the Bus.
 *
 * Here is a sample implementation of the main method:
 * \code
void BundleGeneric::busMessage( const Message& message, const Bus* source )
 {
	if ( message.isEmpty() ) return;
	if ( !MessageFilter::isMessageTarget(message, this) )
		return;
	if ( !message.getContent()->isRequest() )
		return;
	Message* reply = MPC_busMessageRequest( message, this->Methods, this->Log );
	if ( reply == NULL )
		return;
	BusCore.postMessage(reply);
 }
 * \endcode
 */
class IBusListener
 {
	public:
		/**
		 * @brief  Method raised by the Bus when it processes a new message.
		 *         Implementations will probably analyze the message and perform actions.
		 * @param [in] message the incoming message
		 * @param [in] source the source Bus of the incoming message
		 * @warning The current thread is own and managed by the caller Bus. 
		 *          Heavy, dangerous or time expensive operations should be
		 *          executed asynchronously and not inline!
		 */
		virtual void busMessage( const Message& message, const Bus* source ) = 0;


		virtual void busEvent( Bus::BUS_STATE state, const Bus* source ) {
			(void)state; (void)source;
		 }

	public:
		virtual ~IBusListener() { }

}; // class def


class BusDebugInfo {

	public:
		BusDebugInfo()
			: enabled(true), messageId(0) {
		 }
		virtual ~BusDebugInfo() { }

	public:
		inline bool isEnabled() const				{ return this->enabled; }

		void setCurrentMessage( const Message* message );
		void setCurrentListener( IBusListener* listener );

		std::string getCurrentListener() const;
		int64 getMessageId() const;

		inline void transactionBeginRead() const	{ this->mux.lockForRead(); }
		inline void transactionBeginWrite() const	{ this->mux.lockForWrite(); }
		inline void transactionEnd() const			{ this->mux.unLock(); }

		inline MutexRW* getMutex() const		{ return &this->mux; }

	protected:
		bool enabled;
		mutable MutexRW mux;
		int64 messageId;
		std::string currentListener;


}; // class def



}; // namespace wosh

#endif //__WOSH_Core_Bus_H__
